package com.swap.business.constants;

import com.swap.business.config.CustomizeException;
import com.swap.business.config.DeviceBasicInfoOidConfig;
import com.swap.business.config.DeviceHardwareOidConfig;
import com.swap.business.config.DevicePortOidConfig;
import com.swap.business.domain.SwapDevice;
import com.swap.business.dto.DeviceDto;
import com.swap.business.dto.NetworkDto;
import com.swap.business.dto.SnmpMessageDTO;
import com.swap.business.enums.ManufacturerEnum;
import com.swap.common.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snmp4j.PDU;
import org.snmp4j.Target;
import org.snmp4j.mp.SnmpConstants;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class SnmpDeviceDataAcquisition extends SnmpConfiguration {

    public static final String PORT_ERROR_NAME = "GigabitEthernet";
    public static final String PORT_ERROR_NAME1 = "Ten-GigabitEthernet";
    public static final String PORT_ERROR_NAME2 = "XGigabitEthernet";
    public static final String PORT_ERROR_NAME3 = "Port";
    public static final String PORT_ERROR_NAME4 = "Board";
    private static final Logger logger = LoggerFactory.getLogger(SnmpDeviceDataAcquisition.class);
    private static final int PDU_GET = PDU.GET;
    private static final int PDU_WALK = PDU.GETBULK;
    private static final int PORT_OPEN = 1;
    SnmpMessageDTO snmpMessageDTO;
    Target target;

    public SnmpDeviceDataAcquisition(SnmpMessageDTO snmpMessageDTO) {
        this.snmpMessageDTO = snmpMessageDTO;
    }

    /**
     * M转换bytes
     *
     * @param valueWithMUnit
     * @return
     */
    public static long megabytesToBytes(String valueWithMUnit) {
        if (valueWithMUnit == null || valueWithMUnit.isEmpty()) {
            throw new IllegalArgumentException("Value cannot be null or empty");
        }

        // 移除M单位
        String valueWithoutUnit = valueWithMUnit.replace("M", "");

        try {
            // 将字符串转换为数字，并乘以1024*1024（1MB）
            double megabytes = Double.parseDouble(valueWithoutUnit);
            return (long) (megabytes * 1024 * 1024);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("Invalid value format", e);
        }
    }

    /**
     * 初始化SNMP参数
     */
    public void init() {
        String deviceIp = "udp:" + snmpMessageDTO.getIp() + "/161";
        this.target = createTarget(snmpMessageDTO.getVersion(), snmpMessageDTO.getCommunity(), deviceIp);
        if (snmpMessageDTO.getVersion() == SnmpConstants.version3) {
            initSnmpV3(snmpMessageDTO.getUsername(), snmpMessageDTO.getAuthPassword(), snmpMessageDTO.getPrivPassword());
        } else {
            initSnmpV2();
        }
    }

    /**
     * 关闭SNMP开启的服务
     */
    public void snmpClose() {
        try {
            snmp.close();
            transportMapping.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 采集设备基础信息
     *
     * @param device
     * @return
     */
    public DeviceDto acquireDeviceInfo(SwapDevice device) throws CustomizeException {
        init();
        DeviceDto deviceDto = new DeviceDto();

        // 查询使用的oid
        ArrayList<String> cmhList = new ArrayList<>();
        List<String> baseEntryMap = new ArrayList<>();
        String fanStr = "";
        List<String> fanMap = new ArrayList<>();
        try {
            if (device.getManufacturer().contains(ManufacturerEnum.H3C.name())) {
                cmhList.add(DeviceHardwareOidConfig.cpuUsageH3c);
                cmhList.add(DeviceHardwareOidConfig.memoryUsageH3c);
                cmhList.add(DeviceHardwareOidConfig.temperatureH3c);

                baseEntryMap = createTable(DeviceHardwareOidConfig.physicalNameH3c, PDU_GET, target); //实体型号信息
                try {
                    fanStr = createPDU(DeviceHardwareOidConfig.fanStatusH3c, PDU_GET, target);
                } catch (Exception e) {
                    fanStr = "noSuchInstance";
                }
                fanMap = createTable(DeviceHardwareOidConfig.fanStatusH3c, PDU_GET, target); //风扇
            } else if (device.getManufacturer().contains(ManufacturerEnum.HUAWEI.name())) {
                cmhList.add(DeviceHardwareOidConfig.cpuUsageHuaWei);
                cmhList.add(DeviceHardwareOidConfig.memoryUsageHuaWei);
                cmhList.add(DeviceHardwareOidConfig.temperatureHuaWei);

                baseEntryMap = createTable(DeviceHardwareOidConfig.physicalNameHuaWei, PDU_GET, target); //实体型号信息
                try {
                    fanStr = createPDU(DeviceHardwareOidConfig.fanStatusHuaWei, PDU_GET, target);
                } catch (Exception e) {
                    fanStr = "noSuchInstance";
                }
                fanMap = createTable(DeviceHardwareOidConfig.fanStatusHuaWei, PDU_GET, target); //风扇
            } else if (device.getManufacturer().contains(ManufacturerEnum.FUTUREMATRIX.name())) {
                cmhList.add(DeviceHardwareOidConfig.cpuUsageFutureMatrix);
                cmhList.add(DeviceHardwareOidConfig.memoryUsageFutureMatrix);
                cmhList.add(DeviceHardwareOidConfig.temperatureFutureMatrix);

                baseEntryMap = createTable(DeviceHardwareOidConfig.physicalNameFutureMatrix, PDU_GET, target); //实体型号信息
                try {
                    fanStr = createPDU(DeviceHardwareOidConfig.fanStatusFutureMatrix, PDU_GET, target);
                } catch (Exception e) {
                    fanStr = "noSuchInstance";
                }
                fanMap = createTable(DeviceHardwareOidConfig.fanStatusFutureMatrix, PDU_GET, target); //风扇
            }

            ArrayList<String> portList = new ArrayList<>();
            portList.add(DeviceBasicInfoOidConfig.sysDesc); // 端口名称
            portList.add(DeviceBasicInfoOidConfig.inOctets); // 端口入流量
            portList.add(DeviceBasicInfoOidConfig.outOctets); // 端口出流量
            portList.add(DeviceBasicInfoOidConfig.operStatus); // 端口状态

            String zhanStr = "";
            try {
                zhanStr = createPDU(DeviceBasicInfoOidConfig.zhanDesc, PDU_GET, target);
            } catch (Exception e) {
                zhanStr = "noSuchInstance";
            }
            if (Objects.equals(zhanStr, "noSuchInstance") || Objects.equals(zhanStr, "noSuchObject")) {
                try {
                    zhanStr = createPDU(DeviceBasicInfoOidConfig.zhanDesc1, PDU_GET, target);
                } catch (Exception e) {
                    zhanStr = "";
                }
            }

            Map<Integer, List<String>> map = createTables(cmhList, PDU_GET, target);
            Map<Integer, List<String>> flowMap = createTables(portList, PDU_GET, target);

            List<String> infoDuplicates = baseEntryMap.stream().distinct().collect(Collectors.toList());
            if (!infoDuplicates.isEmpty()) {
                deviceDto.setEquipmentUuId(infoDuplicates.get(0) + "_" + zhanStr);
            }

            if (Objects.equals(fanStr, "noSuchInstance") || Objects.equals(fanStr, "noSuchObject")) {
                deviceDto.setFanStatus("");
            } else {
                if (!fanMap.isEmpty()) {
                    fanMap.removeAll(Collections.singleton(null));
                    fanMap.removeAll(Collections.singleton(" "));
                    fanMap.removeAll(Collections.singleton(""));
                    deviceDto.setFanStatus(String.join(",", fanMap));
                } else {
                    deviceDto.setFanStatus("");
                }
            }

            double cpuSum = 0;
            double memorySum = 0;
            double temp = 0;
            for (Integer key : map.keySet()) {
                List<String> valueList = map.get(key);
                if (Integer.parseInt(valueList.get(0)) != 0) {
                    cpuSum = Double.parseDouble(valueList.get(0));
                    memorySum = Double.parseDouble(valueList.get(1));
                    temp = Double.parseDouble(valueList.get(2));
                }
            }
            deviceDto.setCpuUsageRate(cpuSum);
            deviceDto.setMemoryUsageRate(memorySum);
            deviceDto.setTemp(temp);
//        deviceDto.setCollectTime(DateUtils.getSysTimeInfo());
            deviceDto.setCollectTime(DateUtils.getNowDate());

            //network
            ArrayList<NetworkDto> resultList = new ArrayList<>();
            int interfaceId = 1;
            for (Integer keys : flowMap.keySet()) {
                List<String> valueList = flowMap.get(keys);
                NetworkDto portFlow = new NetworkDto();
                if (valueList.get(0).startsWith(PORT_ERROR_NAME) || valueList.get(0).startsWith(PORT_ERROR_NAME1) || valueList.get(0).startsWith(PORT_ERROR_NAME2)) {
                    if (!valueList.get(0).contains(".") && valueList.size() > 1) {
//                    String interfaceId = this.getLastSlashContent(valueList.get(0));
                        portFlow.setInterfaceId(interfaceId);
                        portFlow.setInterfaceDesc(valueList.get(0));
                        portFlow.setStatus(Integer.valueOf(valueList.get(3)));
//                    portFlow.setRecTime(DateUtils.getTime());
                        portFlow.setRecTime(getSysTimeInfo());
                        Long inboundTraffic = Long.parseLong(valueList.get(1));
                        Long outboundTraffic = Long.parseLong(valueList.get(2));
                        portFlow.setInboundTraffic(inboundTraffic);
                        portFlow.setOutboundTraffic(outboundTraffic);

                        resultList.add(portFlow);
                        interfaceId++;
                    }
                }
            }
            deviceDto.setNetwork(resultList);
        } catch (Exception e) {
            logger.error("设备[{}]信息获取失败: ", device.getId(), e);
            throw new CustomizeException("设备信息获取失败");
        }

        snmpClose();
        return deviceDto;
    }

    /**
     * 采集所有端口入流量
     *
     * @param device
     * @return
     */
    public Map<String, Long> acquirePortInFlow(SwapDevice device) {
        init();
        Map<String, Long> flowRateMap = new LinkedHashMap<>();

        ArrayList<String> oidList = new ArrayList<>();

        oidList.add(DeviceBasicInfoOidConfig.sysDesc); // 端口名称
        oidList.add(DeviceBasicInfoOidConfig.inOctets); // 端口入流量
        Map<Integer, List<String>> InFlowMap = createTables(oidList, PDU_GET, target);

        for (Integer key : InFlowMap.keySet()) {
            List<String> valueList = InFlowMap.get(key);
            if (valueList.get(0).startsWith(PORT_ERROR_NAME) || valueList.get(0).startsWith(PORT_ERROR_NAME1) || valueList.get(0).startsWith(PORT_ERROR_NAME2)) {
                flowRateMap.put(valueList.get(0), Long.parseLong(valueList.get(1)));
            }
        }
        snmpClose();

//        return flowFiltration(flowRateMap, device);
        return flowRateMap;
    }

    /**
     * 计算带宽占用率-huawei
     *
     * @param device
     * @param networkDtos
     * @return
     */
    public List<NetworkDto> getMonitorRateForHw(SwapDevice device, List<NetworkDto> networkDtos) {
        init();
        ArrayList<String> list = new ArrayList<>();
        list.add(DeviceBasicInfoOidConfig.sysDesc); // 端口名称
        if (device.getManufacturer().contains(ManufacturerEnum.HUAWEI.name())) {
            list.add(DevicePortOidConfig.monitorInputRateHuaWei); // 入方向带宽占用率
            list.add(DevicePortOidConfig.monitorOutputRateHuaWei); // 出方向带宽占用率
        } else {
            list.add(DevicePortOidConfig.monitorInputRateFutureMatrix); // 入方向带宽占用率
            list.add(DevicePortOidConfig.monitorOutputRateFutureMatrix); // 出方向带宽占用率
        }

        Map<Integer, List<String>> map = createTables(list, PDU_GET, target);
        ArrayList<NetworkDto> resultList = new ArrayList<>();
        int count = 0;
        for (Integer key : map.keySet()) {
            List<String> value = map.get(key);
            if (value.get(0).startsWith(PORT_ERROR_NAME) || value.get(0).startsWith(PORT_ERROR_NAME1) || value.get(0).startsWith(PORT_ERROR_NAME2)) {
                if (!value.get(0).contains(".")) {
                    NetworkDto networkDto = networkDtos.get(count);
                    count++;

                    networkDto.setInboundBandwidthRate(BigDecimal.valueOf(Long.parseLong(value.get(1))));
                    networkDto.setOutboundBandwidthRate(BigDecimal.valueOf(Long.parseLong(value.get(2))));

                    resultList.add(networkDto);
                }
            }
        }
        snmpClose();
        return resultList;
    }

    /**
     * 获取带宽上限
     *
     * @param device
     * @return
     */
    public Integer getMaxSpeed(SwapDevice device) {
        init();
        ArrayList<String> daiList = new ArrayList<>();
        List<Integer> speeds = new ArrayList<>();
        daiList.add(DeviceBasicInfoOidConfig.sysDesc);
        daiList.add(DeviceBasicInfoOidConfig.highSpeed);
        Map<Integer, List<String>> dai = createTables(daiList, PDU_GET, target);
        for (Integer key : dai.keySet()) {
            List<String> valueList = dai.get(key);
            if (valueList.get(0).startsWith(PORT_ERROR_NAME) || valueList.get(0).startsWith(PORT_ERROR_NAME1) || valueList.get(0).startsWith(PORT_ERROR_NAME2)) {
                speeds.add(Integer.parseInt(valueList.get(1)));
            }
        }
        speeds = speeds.stream().distinct().collect(Collectors.toList());
        return Collections.max(speeds);
    }

    /**
     * 计算入口包转发错包率
     *
     * @param device
     * @return
     */
    public Map<Integer, List<String>> acquirePortInFlowErrKts(SwapDevice device) {
        init();
        ArrayList<String> inBroadList = new ArrayList<>();
        inBroadList.add(DeviceBasicInfoOidConfig.sysDesc); // 端口名称
        inBroadList.add(DevicePortOidConfig.inErrors); // 端口入口方向错包数
        inBroadList.add(DevicePortOidConfig.inUcastPkts); // 端口入方向单播报文数
        inBroadList.add(DevicePortOidConfig.inMulticastPkts); // 端口入方向组播报文数
        inBroadList.add(DevicePortOidConfig.inBroadcastPkts); // 端口入方向广播报文数
        Map<Integer, List<String>> InFlowMap = createTables(inBroadList, PDU_GET, target);

        snmpClose();
        return InFlowMap;
    }

    /**
     * 计算出口包转发错包率
     *
     * @param device
     * @return
     */
    public Map<Integer, List<String>> acquirePortOutFlowErrKts(SwapDevice device) {
        init();
        ArrayList<String> outBroadList = new ArrayList<>();

        outBroadList.add(DeviceBasicInfoOidConfig.sysDesc); // 端口名称
        outBroadList.add(DevicePortOidConfig.outErrors); // 端口出口方向错包数
        outBroadList.add(DevicePortOidConfig.outUcastPkts); // 端口出方向单播报文数
        outBroadList.add(DevicePortOidConfig.outMulticastPkts); // 端口出方向组播报文数
        outBroadList.add(DevicePortOidConfig.outBroadcastPkts); // 端口出方向广播报文数
        Map<Integer, List<String>> InFlowMap = createTables(outBroadList, PDU_GET, target);

        snmpClose();
        return InFlowMap;
    }

    /**
     * 采集所有端口出流量
     *
     * @param device
     * @return
     */
    public Map<String, Long> acquirePortOutFlow(SwapDevice device) {
        init();
        Map<String, Long> flowRateMap = new LinkedHashMap<>();

        ArrayList<String> oidList = new ArrayList<>();

        oidList.add(DeviceBasicInfoOidConfig.sysDesc); // 端口名称
        oidList.add(DeviceBasicInfoOidConfig.outOctets); // 端口出流量

        Map<Integer, List<String>> OutFlowMap = createTables(oidList, PDU_GET, target);

        for (Integer key : OutFlowMap.keySet()) {
            List<String> valueList = OutFlowMap.get(key);
            if (valueList.get(0).startsWith(PORT_ERROR_NAME) || valueList.get(0).startsWith(PORT_ERROR_NAME1) || valueList.get(0).startsWith(PORT_ERROR_NAME2)) {
                flowRateMap.put(valueList.get(0), Long.parseLong(valueList.get(1)));
            }
        }

        snmpClose();
//        return flowFiltration(flowRateMap, device);
        return flowRateMap;
    }

    /**
     * 设备采集端口过滤
     *
     * @param map
     * @return
     */
    private Map<String, Long> flowFiltration(Map<String, Long> map, SwapDevice device) {
        init();
        Map<String, Long> resultMap = new LinkedHashMap<>();

        // 采集设备所有端口状态信息
        List<String> statusList = createTable(DeviceBasicInfoOidConfig.operStatus, PDU_GET, target);

        if (statusList.size() != map.size()) {
            log.info("<端口异常数据过滤> || 端口数量有误!!!");
            return map;
        }

        // 转换map的keySet集合类型
        ArrayList<String> keyList = new ArrayList<>(map.keySet());
        for (int i = 0; i < keyList.size(); i++) {
            String portName = keyList.get(i);
            int portStatus = Integer.parseInt(statusList.get(i));

            if (portName.startsWith(PORT_ERROR_NAME) || portName.startsWith(PORT_ERROR_NAME1) || portName.startsWith(PORT_ERROR_NAME2)) {
                resultMap.put(portName, map.get(portName));
            }
        }

        snmpClose();
        return resultMap;
    }

    private String getFirstNonEmptyString(List<String> list) {
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String item = iterator.next();
            if (item != null && !item.isEmpty()) {
                return item;
            }
        }
        return "";
    }

    public String getLastSlashContent(String str) {
        int lastIndex = str.lastIndexOf('/');
        if (lastIndex != -1) {
            str = str.substring(lastIndex + 1);
            if (str.length() >= 2) {
                str = str.split(" ")[0];
                return str;
            }
            return str;
        }

        return str;
    }

    public String getSysTimeInfo() {
        LocalDateTime now = LocalDateTime.now(ZoneId.of("GMT+8"));
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        return now.format(formatter);
    }
}
